#include "VRPDocument.h"
#include "RoutingDialog.h"

#include "VRPXml.h"

using namespace boost;

VRPDocument::VRPDocument(QObject *parent) :
    QObject(parent),
    routingParentWidget_(NULL)
{
    depo_.reset();
    nameMap_ = VRPRouteGraphTraits::GetVertexNameMap(graph_);
    bhMap_ = VRPRouteGraphTraits::GetVertexBackhaulMap(graph_);
    coordMap_ = VRPRouteGraphTraits::GetVertexCoordinateMap(graph_);
    demandMap_ = VRPRouteGraphTraits::GetVertexDemandMap(graph_);
    weightMap_ = VRPRouteGraphTraits::GetEdgeWeightMap(graph_);
    routeMap_ = VRPRouteGraphTraits::GetEdgeRouteMap(graph_);
}

VRPDocument::~VRPDocument()
{
}

bool VRPDocument::isEmpty()
{
    if(!depo_)
        return true;
    return false;
}

void VRPDocument::load()
{
    clear();
    VRPXml xml;
    int ret = xml.loadGraph(graph_, vehicleList_, "/home/green/graph.vml");

    if(ret == VRPXml::XML_SUCCESS)
    {
        depo_ = DepoPtr(new Depo(graph_, VRPRouteGraphTraits::GetFirstVertex(graph_)));

        std::pair<VRPRouteGraphTraits::VertexIterator, VRPRouteGraphTraits::VertexIterator> verPair;
        verPair = boost::vertices(graph_);
        verPair.first++;

        std::pair<VRPRouteGraphTraits::VertexIterator, VRPRouteGraphTraits::VertexIterator> vp;
        for(vp = verPair; vp.first != vp.second; vp.first++)
        {
            CustomerPtr customer(new Customer(graph_, *vp.first));
            customerList_.push_back(customer);
        }
    }

    emit loaded();
}

void VRPDocument::save()
{
    VRPXml xml;
    xml.saveGraph(graph_, vehicleList_, "/home/green/graph.vml");
}

void VRPDocument::clear()
{
    CustomerPtr customer;
    foreach(customer, customerList_)
        customer->vertex_ = VRPRouteGraphTraits::null_vertex();
    customerList_.clear();
    vehicleList_.clear();

    if(depo_)
    {
        depo_->vertex_ = VRPRouteGraphTraits::null_vertex();
        depo_.reset();
    }

    graph_.clear();
}

void VRPDocument::setDepo(const QPointF& depoPoint)
{
    if(depo_)
        return;

    VRPRouteGraphTraits::Vertex depoVertex = boost::add_vertex(graph_);

    nameMap_[depoVertex] = "Depo";
    bhMap_[depoVertex] = false;
    CirclePointPtr cp(new qmapcontrol::Point(depoPoint.x(), depoPoint.y(),
                                             VRPPixmapsFactory::instance()->createdDepoPixmap(),
                                             "Depo"));
    coordMap_[depoVertex] = cp;
    demandMap_[depoVertex] = 0;

    depo_ = DepoPtr(new Depo(graph_, depoVertex));
}

DepoPtr VRPDocument::getDepo()
{
    return depo_;
}

bool VRPDocument::rouning(const CustomerInfo &customerInfo,
                          QList< QPair<CustomerPtr, RouteType> >& routeList,
                          RouteType& depoRoute)
{
    RoutingDialog* routingDialog = new RoutingDialog(customerInfo,
                                                     customerList_,
                                                     depo_,
                                                     routeList,
                                                     depoRoute,
                                                     routingParentWidget_);
    int res =routingDialog->exec();
    routingDialog->deleteLater();

    if(res == QDialog::Accepted)
        return true;
    else
        return false;
}

CustomerPtr VRPDocument::addCustomer(const CustomerInfo &customerInfo)
{
    VRPRouteGraphTraits::Vertex customerVertex;
    VRPRouteGraphTraits::Edge routeEdge;
    QList< QPair<CustomerPtr, RouteType> > routeList;
    QPair<CustomerPtr, RouteType> routePair;
    RouteType depoRoute;
    CustomerPtr customer;

    if(!depo_)
        return customer;

    if(!rouning(customerInfo, routeList, depoRoute))
        return customer;

    customerVertex = boost::add_vertex(graph_);
    foreach(routePair, routeList)
    {
        routeEdge = boost::add_edge(customerVertex, routePair.first->vertex_, graph_).first;
        routeMap_[routeEdge] = routePair.second.lineString;
        weightMap_[routeEdge] = routePair.second.weight;
    }

    /// Depo route:
    routeEdge = boost::add_edge(customerVertex, depo_->vertex_, graph_).first;
    routeMap_[routeEdge] = depoRoute.lineString;
    weightMap_[routeEdge] = depoRoute.weight;

    nameMap_[customerVertex] = customerInfo.name.toStdString();
    bhMap_[customerVertex] = customerInfo.isBackhaul;
    QPixmap* customerPixmap = VRPPixmapsFactory::instance()->createCustomerPixmap();
    coordMap_[customerVertex] = CirclePointPtr(new qmapcontrol::Point(customerInfo.coordinate.x(),
                                                                      customerInfo.coordinate.y(),
                                                                      customerPixmap,
                                                                      customerInfo.name));
    demandMap_[customerVertex] = customerInfo.demand;

    customer = CustomerPtr(new Customer(graph_, customerVertex));
    customerList_.push_back(customer);

    return customer;
}

void VRPDocument::removeCustomer(CustomerPtr customer)
{
    /// Check is customer exists and presenr in list
    if(!customer && customer->vertex_ == VRPRouteGraphTraits::null_vertex() && !customerList_.contains(customer))
        return;

    /// Remove all edge with this vertex
    VRPRouteGraphTraits::Edge ed;
    bool isEdge = false;
    std::pair<VRPRouteGraphTraits::VertexIterator, VRPRouteGraphTraits::VertexIterator> vp;
    for(vp = boost::vertices(graph_); vp.first != vp.second; vp.first++)
    {
        boost::tie(ed, isEdge) = boost::edge(*vp.first, customer->vertex_, graph_);
        if(isEdge)
            boost::remove_edge(ed, graph_);
    }

    /// Remove customer vertex
    boost::remove_vertex(customer->vertex_, graph_);

    /// Set null to this vertex
    customer->vertex_ = VRPRouteGraphTraits::null_vertex();

    /// Remove from customer list
    customerList_.removeOne(customer);
}

CustomerPtr VRPDocument::customerAtPoint(CirclePointPtr point)
{
    CustomerPtr retCustomer = CustomerPtr();
    CustomerPtr customer = CustomerPtr();
    foreach(customer, customerList_)
        if(customer->getCoordinate() == point)
        {
            retCustomer = customer;
            break;
        }

    return retCustomer;
}

int VRPDocument::getCustomersCount() const
{
    return customerList_.size();
}

void VRPDocument::setRoutingParentWidget(QWidget *widget)
{
    routingParentWidget_ = widget;
}

QList<CustomerPtr>& VRPDocument::customerListRef()
{
    return customerList_;
}

QList<VehiclePtr>& VRPDocument::vehicleListRef()
{
    return vehicleList_;
}

VehiclePtr VRPDocument::addVehicle()
{
    QString vehicleDefName = "Vehicle #" + QString::number(vehicleList_.size() + 1);
    VehiclePtr vp(new Vehicle(vehicleDefName));
    vehicleList_.push_back(vp);
    return vp;
}
